package problems;

import interfaces.EulerProblemInterface;
import utility.ProblemSpecifics;
import utility.Triplet;

public class EulerProblem009 implements EulerProblemInterface {

	private ProblemSpecifics problemSpecifics;

	private static final int CORRECT_SUM = 1000;

	public EulerProblem009() {
		String problemStatement = "There exists exactly one Pythagorean triplet for which a + b + c = 1000. Find the product abc.";
		problemSpecifics = new ProblemSpecifics(problemStatement);
	}

	public void solve() {
		loopOverAllPossibleTripletsAndCheckForPythagorean();
	}

	private void loopOverAllPossibleTripletsAndCheckForPythagorean() {

		int minimumForA = 0;
		/*
		 * since a is biggest when b is smallest (namely zero) and since a is
		 * smaller than c, a cannot exceed correctSum/2
		 */
		int maximumForA = CORRECT_SUM / 2 - 1;

		for (int a = minimumForA; a <= maximumForA; a++) {
			if (noTripletFoundYet()) {
				loopOverBAndCAndCheckForPythagorean(a);
			}
		}

	}

	private void loopOverBAndCAndCheckForPythagorean(int a) {

		/* since b is strictly greater than a */
		final int minimumForB = a + 1;

		/* since a < b < c */
		int minimalValueForC = 2;

		/* make the sum not exceed 1000 */
		int maximumForB = CORRECT_SUM - minimalValueForC - a;

		for (int b = minimumForB; b <= maximumForB; b++) {
			if (noTripletFoundYet()) {
				loopOverCAndCheckForPythagorean(a, b);
			}
		}

	}

	private void loopOverCAndCheckForPythagorean(int a, int b) {

		/* since c is strictly greater than b */
		int minimumForC = b + 1;
		/* make the sum not exceed 1000 */
		int maximumForC = CORRECT_SUM - b - a;

		for (int c = minimumForC; c <= maximumForC; c++) {
			if (noTripletFoundYet()) {
				checkForPythagorean(a, b, c);
			}
		}

	}

	private void checkForPythagorean(int a, int b, int c) {

		Triplet triplet = new Triplet(a, b, c);

		if (conditionsAreMet(triplet)) {
			long result = triplet.getProduct();
			saveResult(result);
		}
	}

	private boolean noTripletFoundYet() {
		return !problemSpecifics.isSolved();
	}

	private boolean conditionsAreMet(Triplet triplet) {
		return triplet.isPhytagTriplet() && triplet.sumsUpTo1000();
	}

	private void saveResult(long result) {
		problemSpecifics.setResultAndMarkSolved(result);
	}

	public long reportResult() {
		return problemSpecifics.reportResult();
	}

	public ProblemStatus reportStatus() {
		return new ProblemStatus(problemSpecifics.isSolved());
	}

	public String stateTheProblem() {
		return problemSpecifics.stateTheProblem();
	}
}
